package com.xci.platform.web.core;

import com.xci.platform.annotation.AllowAnonymous;
import com.xci.platform.annotation.Authorize;
import com.xci.platform.auth.core.AuthHelper;
import com.xci.platform.core.BoolMessage;
import com.xci.platform.core.Constant;
import com.xci.platform.helper.StringHelper;
import com.xci.platform.helper.TokenHelper;
import com.xci.platform.helper.WebHelper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.lang.reflect.Method;

/**
 * SpringMVC配置
 */
@Slf4j
@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Resource
    protected AuthHelper authHelper;

    /**
     * 添加拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截所有请求判断权限
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/**/login", "/**/js/**", "/**/css/**", "/**/font/**", "/**/img/**", "/**/*.js", "/**/*.css", "/**/*.jpg", "/**/*.png", "/upload/**");//不拦截

        registry.addInterceptor(viewInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/**/js/**", "/**/css/**", "/**/font/**", "/**/img/**", "/**/*.js", "/**/*.css", "/**/*.jpg", "/**/*.png", "/upload/**");//不拦截
    }

    /**
     * 以前要访问一个页面需要先创建个Controller控制类，再写方法跳转到页面
     * 在这里配置后就不需要那么麻烦了，直接访问http://localhost:8080/toLogin就跳转到login.jsp页面了
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //registry.addViewController("/toLogin").setViewName("login");
    }

    /**
     * 配置静态资源访问
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/upload/**").addResourceLocations("file:" + new File("").getAbsolutePath() + "/upload/");
    }

    @Bean
    public HandlerInterceptor authenticationInterceptor() {
        return new HandlerInterceptor(){
            /**
             * 在请求处理之前进行调用（Controller方法调用之前）
             */
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                // 如果不是映射到方法直接放行
                if (!(handler instanceof HandlerMethod)) {
                    return true;
                }
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();

                // 类/方法上有@AllowAnonymous,允许匿名登录,直接放行
                if (method.getAnnotation(AllowAnonymous.class) != null
                        || method.getDeclaringClass().getAnnotation(AllowAnonymous.class) != null) {
                    return true;
                }

                // 类和方法都没有@Authorize,不需要用户认证,直接放行
                Authorize classAuth = method.getDeclaringClass().getAnnotation(Authorize.class);
                Authorize methodAuth = method.getAnnotation(Authorize.class);
                if (methodAuth == null && classAuth == null) {
                    return true;
                }

                //需要认证
                if (WebHelper.isApp(request)) {//api请求
                    String authHeader = request.getHeader("Authorization");
                    if (authHeader == null) {
                        WebHelper.echoJson(new BoolMessage(false, "没有权限"), response);
                        return false;
                    }
                    String token;
                    if (authHeader.startsWith("Bearer ")) {
                        token = authHeader.substring(7);
                    } else {
                        token = authHeader;
                    }

                    try {
                        final Claims claims = TokenHelper.parseJWT(token);
                        request.setAttribute(Constant.ApiTokenKey, claims.getId());

                        boolean classPass = true, methodPass = true;
                        if (classAuth != null && StringHelper.isNotBlank(classAuth.code())) {
                            classPass = authHelper.isAuthorize(classAuth.code());
                        }
                        if (methodAuth != null && StringHelper.isNotBlank(methodAuth.code())) {
                            methodPass = authHelper.isAuthorize(methodAuth.code());
                        }
                        if (!(classPass && methodPass)) {
                            WebHelper.echoJson(new BoolMessage(false, "没有权限,请联系管理员开通!"), response);
                            return false;
                        }

                    } catch (SignatureException e) {
                        WebHelper.echoJson(new BoolMessage(false, "认证签名异常"), response);
                        return false;
                    } catch (ExpiredJwtException e) {
                        WebHelper.echoJson(new BoolMessage(false, "认证签名已过期"), response);
                        return false;
                    } catch (MalformedJwtException e) {
                        WebHelper.echoJson(new BoolMessage(false, "认证签名格式错误"), response);
                        return false;
                    } catch (IllegalArgumentException e) {
                        WebHelper.echoJson(new BoolMessage(false, "认证签名参数错误"), response);
                        return false;
                    } catch (Exception e) {
                        WebHelper.echoJson(new BoolMessage(false, "无效的认证签名"), response);
                        return false;
                    }
                    return true;
                } else {

                    boolean loginResult = authHelper.checkAndAutoLogin(request);
                    if (loginResult) {
                        boolean classPass = true, methodPass = true;
                        String code1 = "", code2 = "";
                        if (classAuth != null && StringHelper.isNotBlank(classAuth.code())) {
                            classPass = authHelper.isAuthorize(classAuth.code());
                            code1 = classAuth.code();
                        }
                        if (methodAuth != null && StringHelper.isNotBlank(methodAuth.code())) {
                            methodPass = authHelper.isAuthorize(methodAuth.code());
                            code2 = methodAuth.code();
                        }
                        if (!(classPass && methodPass)) {
                            if (WebHelper.isAjax(request)) {//ajax请求
                                WebHelper.echoJson(new BoolMessage(false, "没有权限,请联系管理员开通!"), response);
                            } else {//普通web请求
                                String contextPath = request.getContextPath();

                                response.sendRedirect(contextPath + "/unauthorized?code1=" + code1 + "&code2=" + code2);
                            }
                            return false;
                        }
                        return true;
                    }

                    //登录超时
                    if (WebHelper.isAjax(request)) {//ajax请求
                        WebHelper.echoJson(new BoolMessage(false, "登录超时,请重新登录"), response);
                    } else {//普通web请求
                        String contextPath = request.getContextPath();
                        response.sendRedirect(contextPath + "/login");
                    }
                    return false;
                }
            }

            /**
             * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
             */
            @Override
            public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
            }

            /**
             * 在整个请求结束之后被调用，也就是在DispatcherServlet 渲染了对应的视图之后执行（主要是用于进行资源清理工作）
             */
            @Override
            public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
            }
        };
    }

    @Bean
    public HandlerInterceptor viewInterceptor() {
        return new HandlerInterceptor(){
            /**
             * 在请求处理之前进行调用（Controller方法调用之前）
             */
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                return true;
            }

            /**
             * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
             */
            @Override
            public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
                httpServletRequest.setAttribute("helper",authHelper);
            }

            /**
             * 在整个请求结束之后被调用，也就是在DispatcherServlet 渲染了对应的视图之后执行（主要是用于进行资源清理工作）
             */
            @Override
            public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
            }
        };
    }

    // @Bean
    // public FilterRegistrationBean jwtFilter() {
    //     final FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
    //     registrationBean.setFilter(new JwtFilter());
    //     registrationBean.addUrlPatterns("/api/*");
    //     return registrationBean;
    // }
}
